/*
 * 版权所有 (C) 2021 佳牛(WWW.JIANIU.IO) 保留所有权利。 
*/
package org.zhiqim.kernel.global;

import java.util.ArrayList;
import java.util.List;

import org.zhiqim.kernel.ZhiqimI18n;
import org.zhiqim.kernel.model.maps.HashMapCO;
import org.zhiqim.kernel.model.maps.MapCO;
import org.zhiqim.kernel.service.MultiInstance;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Classes;

/**
 * 全局变量管理
 *
 * @version v1.0.0 @author zgzou 2022年2月12日 新建与整理
 */
public class Glb
{
    private final MapCO map = new HashMapCO();

    /********************************************************************************/
    //全局变量获取
    /********************************************************************************/
    
    /** 判断全局变量表中是否有值 */
    public boolean has(Class<?> cls)
    {
        return map.containsKey(cls);
    }
    
    /**
     * 从全局变量表中获取对象，（单例取本身｜多例取第一个）
     * 
     * @param cls   对象类名
     * @return      对象
     */
    @SuppressWarnings("unchecked")
    public <T> T get(Class<T> cls)
    {
        Object obj = map.get(cls);
        if (obj == null)
        {//1.不存在
            return null;
        }
        
        if (!(MultiInstance.class.isAssignableFrom(cls)))
        {//2.单例
            return (T)obj;
        }
        
        //3.多例的，转为列表，为空null，有取第一个
        List<?> list = (List<?>)obj;
        return list.isEmpty()?null:(T)list.get(0);
    }
    
    /**
     * 从全局变量表中获取多例中的一个，（单例时返回null｜多例索引号不正确返回null｜多例索引号正确取值）
     * 
     * @param cls   对象类名
     * @param idx   多例索引
     * @return      对象
     */
    @SuppressWarnings("unchecked")
    public <T> T get(Class<T> cls, int idx)
    {
        Asserts.as(MultiInstance.class.isAssignableFrom(cls)?null:"全局变量表中，不是多例不支持调用该方法[get(Class<T> cls, int idx)]");
        
        Object obj = map.get(cls);
        if (obj == null)
            return null;
        
        List<?> list = (List<?>)obj;
        return (list.size() < (idx+1))?null:(T)list.get(idx);
    }

    /**
     * 从全局变量表中获取对象，（如果没有则尝试创建，创建不成功强制抛出IllegalArgumentException）
     * 
     * @param cls   对象类名
     * @return      对象
     */
    public <T> T getc(Class<T> cls)
    {
        T obj = get(cls);
        if (obj != null)
        {//1.存在即返回
            return obj;
        }
        
        synchronized (map)
        {
            obj = get(cls);
            if (obj != null)
            {//2.在同步中作双重检查
                return obj;
            }
            
            //3.新建实例，并放置到全局对象表中
            obj = Classes.newInstance(cls);
            Asserts.assertNotNull(obj, ZhiqimI18n.classDoesNotSupportNoParameterConstructors, cls.getName());
            
            if (!MultiInstance.class.isAssignableFrom(cls))
            {//单例
                map.put(cls, obj);
            }
            else
            {//多例
                MultiInstance instance = (MultiInstance)obj;
                instance.setIndex(0);
                
                ArrayList<MultiInstance> list = new ArrayList<>();
                list.add(instance);
                list.trimToSize();
                map.put(cls, list);
            }
            
            return obj;
        }
    }
    
    /********************************************************************************/
    //全局变量设置
    /********************************************************************************/
    
    /**
     * 增加对象到全局变量表中，注意只支持增加，不支持修改
     * 
     * @param cls   对象类名
     * @param obj   对象
     */
    public <T> boolean add(Class<T> cls, T obj)
    {
        if (!MultiInstance.class.isAssignableFrom(cls))
        {//单例
            synchronized (map)
            {
                if (map.containsKey(cls))
                    return false;
                
                map.put(cls, obj);
            }
        }
        else
        {//多例
            synchronized (map)
            {
                @SuppressWarnings("unchecked")
                ArrayList<MultiInstance> list = (ArrayList<MultiInstance>)map.get(cls);
                if (list == null)
                {
                    list = new ArrayList<MultiInstance>();
                    map.put(cls, list);
                }
                
                MultiInstance instance = (MultiInstance)obj;
                list.add(instance);
                list.trimToSize();
                if (instance != null)
                {//因为允许对象为null，这里要判断
                    instance.setIndex(list.size() - 1);
                }
            }
        }
        
        return true;
    }
    
    /********************************************************************************/
    //全局变量移除
    /********************************************************************************/
    
    /**
     * 从全局变量表中移除指定的对象
     * 
     * @param cls   类结构
     */
    public void remove(Class<?> cls)
    {
        synchronized (map)
        {
            map.remove(cls);
        }
    }
    
    /**
     * 从全局变量表中移除指定的对象（单例直接移除｜多例置该索引号的值为null）
     * 
     * @param cls   类结构
     * @param obj   对象
     */
    public <T> void remove(Class<T> cls, T obj)
    {
        if (!map.containsKey(cls))
            return;
        
        if (!MultiInstance.class.isAssignableFrom(cls))
        {//单例
            remove(cls);
            return;
        }
        
        //多例
        List<?> list = (List<?>)map.get(cls);
        synchronized (list)
        {
            for (int i=0;i<list.size();i++)
            {
                Object object = list.get(i);
                if (object == obj)
                {//设置该索引号的值为null，判断使用恒等（保证对象实例是同一个，而不是对象值相等）
                    list.set(i, null);
                }
            }
        }
    }
    
    /**
     * 从全局变量表中移除指定的对象（单例直接移除｜多例置该索引号的值为null）
     * 
     * @param cls   类结构
     * @param idx   多例索引值
     */
    public void remove(Class<?> cls, int idx)
    {
        Asserts.as((cls != null)?null:"删除全局变量时[类结构参数]不能为null");
        Asserts.as((idx >= 0)?null:"删除全局变量多例对象时[索引号]不正确");
        
        if (!map.containsKey(cls))
            return;
        
        if (!MultiInstance.class.isAssignableFrom(cls))
        {//单例
            remove(cls);
            return;
        }
        
        //多例
        List<?> list = (List<?>)map.get(cls);
        if (list.size() <= idx)
            return;
        
        synchronized (list)
        {//设置该索引号的值为null
            list.set(idx, null);
        }
    }
}
